package pacBioSimple;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import tools.kmer.KmerSet_binary;
import tools.sequences.sequenceUtils;

public class PacBioLinkerScreenKmer {

	Entropy<Character> entropy= null;
	PrintWriter stat = null;
	HashMap<Integer, Integer>kmer_val_app=null;
	HashMap<Integer, Integer>reverse_size_distr=null;
	public PacBioLinkerScreenKmer() 
	{
		List<Character> alphabet = new ArrayList<Character>();
		alphabet.add('A');
		alphabet.add('C');
		alphabet.add('G');
		alphabet.add('T');
		entropy = new Entropy<Character>(alphabet);
		kmer_val_app= new HashMap<Integer, Integer>();
		reverse_size_distr = new HashMap<Integer, Integer>();
//		this.sequence=seq;	
//		ks = new KmerSet_binary(kmer_size);
//		screen_seq();
	}
	
	public void cut_and_align(List<Double>kmerD, PacBioReadContainer read) throws Exception
	{
		List<StringBuilder> toBeAlign=new ArrayList<StringBuilder>();
		
		double prev=-1;
		int pos=-1;
		
		for(int i=0; i <kmerD.size();++i)
		{
			if(prev==-1)
			{
				prev=kmerD.get(i);
				pos=i;
			}
			else if( prev != kmerD.get(i))
			{
				toBeAlign.add(new StringBuilder(read.getOrginalSequence().substring(pos, i)));
				prev=kmerD.get(i);
				pos=i;
			}
		}
		if(pos < kmerD.size())
		{
			toBeAlign.add(new StringBuilder(read.getOrginalSequence().substring(pos, kmerD.size())));	
		}
		
		if(toBeAlign.size()>1)
		{
			//here we have to align some stuffs
			int longest_subread=0;
			int longest_subread_pos=0;
			PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("temp_input.fasta")));
			
			for(int i=0; i <toBeAlign.size();++i)
				{
					pw.println(">"+i);
					pw.println(toBeAlign.get(i));
					if(longest_subread<toBeAlign.get(i).length())
					{
						longest_subread=toBeAlign.get(i).length();
						longest_subread_pos=i;
					}
				}
				pw.flush();
				pw.close();
				//call mafft
				java.lang.Runtime rt = java.lang.Runtime.getRuntime();
				// Start a new process: UNIX command ls
		        java.lang.Process p = rt.exec("/projects/dep_coupland/grp_nordstrom/bin/mafft/bin/mafft --preservecase --adjustdirectionaccurately  --ep 0.123 --quiet temp_input.fasta");
		        // You can or maybe should wait for the process to complete
		        p.waitFor();
		       // System.out.println("Process exited with code = " + p.exitValue());
		        // Get process' output: its InputStream
		        java.io.InputStream is = p.getInputStream();
		        java.io.BufferedReader reader = new java.io.BufferedReader(new InputStreamReader(is));
		        // And print each line
		        String s = null;
		        PacBioFastaMSAContainer msaContainer = new PacBioFastaMSAContainer();
		        
		        boolean found=false;
		        StringBuilder seq = new StringBuilder();
		        while ((s = reader.readLine()) != null) 
		        {
		        	//System.out.println(s);
		        	if(s.startsWith(">"))
		        	{
		        		if(found==true)
		        		{
		        			msaContainer.readFastaFile(seq.toString());
		        			seq = new StringBuilder();
		        		}
		        		else
		        		{
		        			found=true;
		        		}
		        		seq.append(reader.readLine());
		        	}
		        	else if(found==true)
		        	{
		        		seq.append(s);
		        	}
		        }
		        if(seq.length()>0)
		        {
		    		msaContainer.readFastaFile(seq.toString());
		        }
		        is.close();
		        int conv=msaContainer.getMaximumNumberConservation(90.0);
		        StringBuilder sequence = new StringBuilder();
		        if(conv>20)
		        {
		        	sequence = msaContainer.getConsensusSequence();
		        }
		        else
		        {
		        	//get the longest one
		        	sequence=toBeAlign.get(longest_subread_pos);	        	
		        }
		        read.setOrginalSequence(sequence);		
		}
	}
	
	public void write_stat(String filename)
	{
		try {
			stat = new PrintWriter(new BufferedWriter(new FileWriter(filename)));
			for(Map.Entry<Integer, Integer>entry : kmer_val_app.entrySet())
			{
				stat.println(entry.getKey()+" "+entry.getValue());
			}
			stat.flush();
			stat.close();
			stat= new PrintWriter(new BufferedWriter(new FileWriter(filename+"_length_distr")));
			for(Map.Entry<Integer, Integer>entry : reverse_size_distr.entrySet())
			{
				stat.println(entry.getKey()+" "+entry.getValue());
			}
			stat.flush();
			stat.close();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public List<Double> conntect_ones(int width, ArrayList<Double>kmerD)
	{
		int prev_pos=-1;
		double success = width*kmerD.size()/100;
		for(int i=0; i < kmerD.size();++i)
		{
			if(kmerD.get(i)==1)
			{
				if(prev_pos!=-1)
				{
					int distance=i-prev_pos;
					if(distance<=Math.round(success))
					{
						for(int j=prev_pos;j<=i;++j)
						{
							kmerD.set(j, 1.0);
						}
					}
					else
					{
						kmerD.set(prev_pos, 0.0);
					}
				}
				prev_pos=i;
			}
			
		}
		return kmerD;
	}
	
	public List<Double> checkBlockWidth(double width, ArrayList<Double>kmerD)
	{
		int prev_pos=-1;
		int number_of_times=0;
		int counter=0;
		double success = width*kmerD.size()/100;
		for(int i=0; i < kmerD.size();++i)
		{
			if(kmerD.get(i)==1 && prev_pos==-1)
			{
				prev_pos=i;
			}
			else if(kmerD.get(i)==0 && prev_pos!=-1)
			{
				if(counter<Math.round(success))
				{
					for(int j=prev_pos;j<i;++j)
					{
						kmerD.set(j, 0.0);
					}
				}
				else
				{
					++number_of_times;
				}
				MyUtil.addToMap(reverse_size_distr, counter);
				counter=0;
				prev_pos=-1;
			}
			else if(kmerD.get(i)==1 && prev_pos!=-1)
			{
				++counter;
			}
		}
		
		if(number_of_times<1 && prev_pos!=-1)
		{
			kmerD.set(prev_pos, 0.0);
		}
		return kmerD;
	}
	
	
	public List<Double> slinding_window(int sw,ArrayList<Double>kmerD)
	{
		ArrayList<Double> swList=new ArrayList<Double>();
		for(int i=0; i<kmerD.size()-sw;++i)
		{
			double sum=0;
			for(int j=i;j<i+sw;++j )
			{
//				if(kmerD.get(j)==1.0)
//				{
//					System.out.println(j);
//				}
				sum+=kmerD.get(j);
			}
			sum=sum/(double)sw;
			swList.add(sum);
		}
		double sum =0;
		for(int j=kmerD.size()-sw;j<kmerD.size();++j )
		{
			sum+=kmerD.get(j);
		}
		sum=sum/(double)(sw);
		for(int j=kmerD.size()-sw;j<kmerD.size();++j )
		{
			swList.add(sum);
		}
		return swList;
	}
	
	public boolean screen_seq(StringBuilder seq,int kmer_size,double entropy_cutoff,List<Double>kmerD)
	{
		KmerSet_binary ks = new KmerSet_binary(kmer_size);
//		boolean pattern_path_r=false;
//		boolean pattern_path_f=true;
		boolean linker_dectected=false;
//		boolean direction =true;
		
		//int linker_change_counter=0;
		int counter=0;
		//int kmer_counter=0;		
		for(int i=0; i<=seq.length()-kmer_size;++i)
		{
			String kmer = seq.substring(i,i+kmer_size);
			boolean seen_f = ks.exists(kmer);
			boolean seen_r=false;
			boolean found_kmer=false;
			if(!seen_f)
			{
				//check if maybe the reverse has been seen
				seen_r=ks.exists(sequenceUtils.reverseComplement(kmer));
				if(!seen_r)
				{
					if(i==seq.length()-kmer_size)
					{
						for(int t=0; t<kmer_size;++t)
						{
							kmerD.add(0.0);
						}
					}
					else
					{
						kmerD.add(0.0);
						ks.addKmerIterative(kmer);	
					}
				}
				else
				{
					if(i==seq.length()-kmer_size)
					{
						for(int t=i; t<kmer_size;++t)
						{
							kmerD.add(1.0);
						}
					}
					else
					{
						kmerD.add(1.0);
						found_kmer=true;
					}
				}
			}
			else
			{
				if(i==seq.length()-kmer_size)
				{
					for(int t=i; t<kmer_size;++t)
					{
						kmerD.add(0.0);
					}
				}
				else
				{
					kmerD.add(0.0);
					ks.addKmerIterative(kmer);
					found_kmer=true;
				}
				
			}
			
			if(found_kmer)
			{
				//check now for repeat
				double entropy_newSeed=entropy.calcEntropy(kmer); 
				if(entropy_newSeed>=entropy_cutoff)
				{
					if(!seen_f && seen_r)
					{
						++counter;
					}
					if(counter>4)
					{
						linker_dectected=true;
					}
					int kmer_val=ks.addKmerIterative(kmer);
					MyUtil.addToMap(kmer_val_app, kmer_val);
				}
			}		
		}
		if(linker_dectected)
		{
			return true;
		}
		else
		{
			return false;
		}
		
	}
}
